package com.example.core.filter.loadbalance;

import com.alibaba.fastjson.JSON;
import com.example.common.config.Rule;
import com.example.common.config.ServiceInstance;
import com.example.common.exception.NotFoundException;
import com.example.core.context.GatewayContext;
import com.example.core.filter.Filter;
import com.example.core.filter.FilterAspect;
import com.example.core.filter.loadbalance.impl.RandomLoadBalanceRule;
import com.example.core.filter.loadbalance.impl.RoundRobinLoadBalanceRule;
import com.example.core.request.GatewayRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.util.Map;
import java.util.Set;

import static com.example.common.constants.FilterConst.*;
import static com.example.common.enums.ResponseCode.SERVICE_INSTANCE_NOT_FOUND;

/**
 * @Author Peng Yisheng
 * @Date 2024/12/3 15:46
 * @Description 负载均衡过滤器
 */
@Slf4j
@FilterAspect(id = LOAD_BALANCE_FILTER_ID,
        name = LOAD_BALANCE_FILTER_NAME,
        order = LOAD_BALANCE_FILTER_ORDER)
public class LoadBalanceFilter implements Filter {

    @Override
    public void doFilter(GatewayContext ctx) {
        String serviceId = ctx.getUniqueId();
        // 拿到网关请求对应的负载均衡规则
        ILoadBalanceRule gatewayLoadBalanceRule = getLoadBalanceRule(ctx);
        // 选择调用的服务实例
        ServiceInstance serviceInstance = gatewayLoadBalanceRule.choose(serviceId, ctx.isGray());
//        log.info("LoadBalanceFilter select host:{}:{}", serviceInstance.getIp(), serviceInstance.getPort());

        GatewayRequest request = ctx.getRequest();
        if (serviceInstance == null || request == null) {
            log.warn("No instance available for :{}", serviceId);
            throw new NotFoundException(SERVICE_INSTANCE_NOT_FOUND);
        }

        String host = serviceInstance.getIp() + ":" + serviceInstance.getPort();
        request.setModifyHost(host);
    }

    /**
     * 根据配置获取负载均衡器
     *
     * @param ctx
     * @return
     */
    public ILoadBalanceRule getLoadBalanceRule(GatewayContext ctx) {
        ILoadBalanceRule loadBalanceRule = null;
        Rule configRule = ctx.getRule();
        if (configRule == null) {
            return null; //TODO: 异常处理
        }
        Set<Rule.FilterConfig> filterConfigs = configRule.getFilterConfigs();
        for (Rule.FilterConfig filterConfig : filterConfigs) {
            String filterId = filterConfig.getId();
            // 匹配负载均衡过滤器id
            if (!filterId.equals(LOAD_BALANCE_FILTER_ID)) {
                continue;
            }
            String config = filterConfig.getConfig();  // json格式 "config": "{\"timeOut\": 500, \"balance\": \"random\"}"
            String strategy = LOAD_BALANCE_STRATEGY_RANDOM;
            if (StringUtils.isNotEmpty(config)) {
                Map<String, String> mapTypeMap = JSON.parseObject(config, Map.class);
                strategy = mapTypeMap.getOrDefault(LOAD_BALANCE_KEY, strategy);
            }
            switch (strategy) {
                case LOAD_BALANCE_STRATEGY_RANDOM:
                    loadBalanceRule = RandomLoadBalanceRule.getInstance(configRule.getServiceId());
                    break;
                case LOAD_BALANCE_STRATEGY_ROUND_ROBIN:
                    loadBalanceRule = RoundRobinLoadBalanceRule.getInstance(configRule.getServiceId());
                    break;
                default:
                    log.warn("No loadBalance strategy for service:{}", strategy);
                    loadBalanceRule = RandomLoadBalanceRule.getInstance(configRule.getServiceId());
                    break;
            }
        }
        return loadBalanceRule;
    }
}
